{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# importing some basic libraries\n",
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib import style\n",
    "style.use('ggplot')\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {},
   "outputs": [],
   "source": [
    "class SVM(object):\n",
    "    def __init__(self,visualization=True):\n",
    "        self.visualization = visualization\n",
    "        self.colors = {1:'r',-1:'b'}\n",
    "        if self.visualization:\n",
    "            self.fig = plt.figure()\n",
    "            self.ax = self.fig.add_subplot(1,1,1)\n",
    "    \n",
    "    def fit(self,data):\n",
    "        #train with data\n",
    "        self.data = data\n",
    "        # { |\\w\\|:{w,b}}\n",
    "        opt_dict = {}\n",
    "        \n",
    "        transforms = [[1,1],[-1,1],[-1,-1],[1,-1]]\n",
    "        \n",
    "        all_data = np.array([])\n",
    "        for yi in self.data:\n",
    "            all_data = np.append(all_data,self.data[yi])\n",
    "                    \n",
    "        self.max_feature_value = max(all_data)         \n",
    "        self.min_feature_value = min(all_data)\n",
    "        all_data = None\n",
    "        \n",
    "        #with smaller steps our margins and db will be more precise\n",
    "        step_sizes = [self.max_feature_value * 0.1,\n",
    "                      self.max_feature_value * 0.01,\n",
    "                      #point of expense\n",
    "                      self.max_feature_value * 0.001,]\n",
    "        \n",
    "        #extremly expensise\n",
    "        b_range_multiple = 5\n",
    "        #we dont need to take as small step as w\n",
    "        b_multiple = 5\n",
    "        \n",
    "        latest_optimum = self.max_feature_value*10\n",
    "        \n",
    "        \"\"\"\n",
    "        objective is to satisfy yi(x.w)+b>=1 for all training dataset such that ||w|| is minimum\n",
    "        for this we will start with random w, and try to satisfy it with making b bigger and bigger\n",
    "        \"\"\"\n",
    "        #making step smaller and smaller to get precise value\n",
    "        for step in step_sizes:\n",
    "            w = np.array([latest_optimum,latest_optimum])\n",
    "            \n",
    "            #we can do this because convex\n",
    "            optimized = False\n",
    "            while not optimized:\n",
    "                for b in np.arange(-1*self.max_feature_value*b_range_multiple,\n",
    "                                   self.max_feature_value*b_range_multiple,\n",
    "                                   step*b_multiple):\n",
    "                    for transformation in transforms:\n",
    "                        w_t = w*transformation\n",
    "                        found_option = True\n",
    "                        \n",
    "                        #weakest link in SVM fundamentally\n",
    "                        #SMO attempts to fix this a bit\n",
    "                        # ti(xi.w+b) >=1\n",
    "                        for i in self.data:\n",
    "                            for xi in self.data[i]:\n",
    "                                yi=i\n",
    "                                if not yi*(np.dot(w_t,xi)+b)>=1:\n",
    "                                    found_option=False\n",
    "                        if found_option:\n",
    "                            \"\"\"\n",
    "                            all points in dataset satisfy y(w.x)+b>=1 for this cuurent w_t, b\n",
    "                            then put w,b in dict with ||w|| as key\n",
    "                            \"\"\"\n",
    "                            opt_dict[np.linalg.norm(w_t)]=[w_t,b]\n",
    "                \n",
    "                #after w[0] or w[1]<0 then values of w starts repeating itself because of transformation\n",
    "                #Think about it, it is easy\n",
    "                #print(w,len(opt_dict)) Try printing to understand\n",
    "                if w[0]<0:\n",
    "                    optimized=True\n",
    "                    print(\"optimized a step\")\n",
    "                else:\n",
    "                    w = w-step\n",
    "                    \n",
    "            # sorting ||w|| to put the smallest ||w|| at poition 0 \n",
    "            norms = sorted([n for n in opt_dict])\n",
    "            #optimal values of w,b\n",
    "            opt_choice = opt_dict[norms[0]]\n",
    "\n",
    "            self.w=opt_choice[0]\n",
    "            self.b=opt_choice[1]\n",
    "            \n",
    "            #start with new latest_optimum (initial values for w)\n",
    "            latest_optimum = opt_choice[0][0]+step*2\n",
    "    \n",
    "    def predict(self,features):\n",
    "        #sign(x.w+b)\n",
    "        classification = np.sign(np.dot(np.array(features),self.w)+self.b)\n",
    "        if classification!=0 and self.visualization:\n",
    "            self.ax.scatter(features[0],features[1],s=200,marker='*',c=self.colors[classification])\n",
    "        return (classification,np.dot(np.array(features),self.w)+self.b)\n",
    "    \n",
    "    def visualize(self):\n",
    "        [[self.ax.scatter(x[0],x[1],s=100,c=self.colors[i]) for x in data_dict[i]] for i in data_dict]\n",
    "        \n",
    "        # hyperplane = x.w+b (actually its a line)\n",
    "        # v = x0.w0+x1.w1+b -> x1 = (v-w[0].x[0]-b)/w1\n",
    "        #psv = 1     psv line ->  x.w+b = 1a small value of b we will increase it later\n",
    "        #nsv = -1    nsv line ->  x.w+b = -1\n",
    "        # dec = 0    db line  ->  x.w+b = 0\n",
    "        def hyperplane(x,w,b,v):\n",
    "            #returns a x2 value on line when given x1\n",
    "            return (-w[0]*x-b+v)/w[1]\n",
    "       \n",
    "        hyp_x_min= self.min_feature_value*0.9\n",
    "        hyp_x_max = self.max_feature_value*1.1\n",
    "        \n",
    "        # (w.x+b)=1\n",
    "        # positive support vector hyperplane\n",
    "        pav1 = hyperplane(hyp_x_min,self.w,self.b,1)\n",
    "        pav2 = hyperplane(hyp_x_max,self.w,self.b,1)\n",
    "        self.ax.plot([hyp_x_min,hyp_x_max],[pav1,pav2],'k')\n",
    "        \n",
    "        # (w.x+b)=-1\n",
    "        # negative support vector hyperplane\n",
    "        nav1 = hyperplane(hyp_x_min,self.w,self.b,-1)\n",
    "        nav2 = hyperplane(hyp_x_max,self.w,self.b,-1)\n",
    "        self.ax.plot([hyp_x_min,hyp_x_max],[nav1,nav2],'k')\n",
    "        \n",
    "        # (w.x+b)=0\n",
    "        # db support vector hyperplane\n",
    "        db1 = hyperplane(hyp_x_min,self.w,self.b,0)\n",
    "        db2 = hyperplane(hyp_x_max,self.w,self.b,0)\n",
    "        self.ax.plot([hyp_x_min,hyp_x_max],[db1,db2],'y--')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#defining a basic data\n",
    "data_dict = {-1:np.array([[1,7],[2,8],[3,8]]),1:np.array([[5,1],[6,-1],[7,3]])}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "optimized a step\n",
      "optimized a step\n",
      "optimized a step\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fad6bfb95f8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "svm = SVM() # Linear Kernel\n",
    "svm.fit(data=data_dict)\n",
    "svm.visualize()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(-1.0, -1.0000000000000979)"
      ]
     },
     "execution_count": 107,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "svm.predict([3,8])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
